package com.github;

import static com.github.DataTypeUtils.isBigNumber;
import static com.github.DataTypeUtils.isDecimal;
import static com.github.DataTypeUtils.isInteger;
import static com.github.DataTypeUtils.toDouble;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Cleanup;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.java.Log;

/**
 * @author lijinting01
 *
 */
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Log
public class ExcelWriter {

	/**
	 * 要写入的workbook
	 */
	@NonNull
	@Getter
	private Workbook workbook;

	/**
	 * 标题写在哪一行
	 */
	private int titleRow = 0;

	/**
	 * 从哪一行开始写，从0计数，默认值为1。
	 */
	private int beginRow = 1;

	/**
	 * 要写入多少行
	 */
	private int rowCount = 100;

	/**
	 * 从哪一列开始写，从0开始计数，默认为0。
	 */
	private int beginColumn = 0;

	/**
	 * @param out
	 * @return
	 * @throws IOException
	 */
	public ExcelWriter writeStream(final OutputStream out) throws IOException {
		workbook.write(out);
		return this;
	}

	/**
	 * @return
	 * @throws IOException
	 */
	public ExcelWriter writeFile(final String fileName) throws IOException {
		@Cleanup
		final OutputStream out = new FileOutputStream(fileName);
		return writeStream(out);

	}

	/**
	 * 如果sheetName存在，则写到sheetname处。如果不存在则新开一个sheet。
	 * 
	 * @param sheetName
	 * @return
	 */
	public <T> ExcelWriter write(final String sheetName, final List<T> beans, final Class<T> beanClass) {

		if (beans == null || beanClass == null) {
			return this;
		}

		if (workbook.getSheetIndex(sheetName) >= 0) {
			return writeSheet(() -> workbook.getSheet(sheetName), beans, beanClass);
		}
		return writeSheet(() -> workbook.createSheet(sheetName), beans, beanClass);
	}

	/**
	 * @param sheetProvider
	 * @param beans
	 * @param beanClass
	 * @return
	 */
	private <T> ExcelWriter writeSheet(final SheetProvider sheetProvider, final List<T> beans, Class<T> beanClass) {
		final Sheet sheet = sheetProvider.provide();
		if (titleRow >= 0) {
			final Row row = sheet.createRow(titleRow);
			final List<String> titles = titleLine(beanClass);
			final int titleCount = titles.size();
			for (int index = 0; index < titleCount; index += 1) {
				Cell cell = row.createCell(beginColumn + index);
				final String element = titles.get(index);
				cell.setCellValue(element);
			}
		}

		final int begin = beginRow;
		final int end = (beans.size() < rowCount) ? beans.size() : rowCount;

		for (int index = 0; index < end; index += 1) {
			T bean = beans.get(index);
			if (bean != null) {// 如果为空则掠过
				createRow(() -> sheet, begin + index, bean, beanClass);
			}
		}
		return this;
	}

	/**
	 * 获取Title行，并且确保每一个Cell都不为null。
	 * 
	 * @return
	 */
	private <T> List<String> titleLine(Class<T> beanClass) {
		final Field[] fields = beanClass.getDeclaredFields();
		final List<String> list = new ArrayList<>();
		for (Field field : fields) {
			CellAt cellAt = field.getDeclaredAnnotation(CellAt.class);
			if (cellAt != null) {
				list.add(cellAt.title() == null ? StringUtils.EMPTY : cellAt.title());
			}
		}
		return list;
	}

	/**
	 * 从Bean创建
	 * 
	 * @param sheet
	 * @param atRow
	 * @param bean
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	@SneakyThrows
	private <T> Row createRow(final SheetProvider sheetProvider, int atRow, T bean, Class<T> beanClass) {
		final Sheet sheet = sheetProvider.provide();
		final Row row = sheet.createRow(atRow);
		final List<Field> fields = Arrays.asList(bean.getClass().getDeclaredFields());
		for (final Field field : fields) {
			createCell(row, field, bean, beanClass);
		}
		return row;
	}

	@SneakyThrows
	private <T> Cell createCell(final Row row, final Field field, final T bean, final Class<T> beanClass) {
		field.setAccessible(true);
		final CellAt cellAt = field.getDeclaredAnnotation(CellAt.class);
		if (field.isSynthetic()) {
			return null;
		}
		if (cellAt != null) {
			return createCell(row, field, cellAt, bean, beanClass);
		}
		log.severe(String.format("field: %s, cellAt:%s, bean:%s, beanClass:%s", field.getName(), cellAt,
				Objects.toString(bean), Objects.toString(beanClass)));
		throw new WorkbookCreationException("无法创建单元格");
	}

	/**
	 * @param row
	 * @param field
	 * @param cellAt
	 * @param bean
	 * @param beanClass
	 * @return
	 */
	@SneakyThrows
	private <T> Cell createCell(final Row row, final Field field, final CellAt cellAt, final T bean,
			final Class<T> beanClass) {
		final String title = cellAt.title();
		final List<String> line = titleLine(beanClass);
		// 从 beginColumn开始往后写
		final int column = beginColumn + (cellAt.index() < 0 ? line.indexOf(title) : cellAt.index());
		final Cell cell = row.createCell(column);

		final Object fieldValue = field.get(bean);
		if (fieldValue == null) // 如果为空则不继续了
			return cell;

		if (field.getType() == String.class) {
			cell.setCellType(CellType.STRING);
			cell.setCellValue((String) fieldValue);
		} else if (field.getType() == boolean.class || field.getType() == Boolean.class) {
			cell.setCellType(CellType.BOOLEAN);
			cell.setCellValue((Boolean) fieldValue);
		} else if (isBigNumber(field.getType()) || isDecimal(field.getType()) || isInteger(field.getType())) {
			cell.setCellType(CellType.NUMERIC);
			cell.setCellValue(toDouble(fieldValue));
		}
		return cell;
	}
}
